﻿


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Milvus.Client;
using System.Threading.Tasks;
using System.Globalization;
using System.Text.Json;
using System.Security.Cryptography.X509Certificates;

namespace RagSharpCore.Service;


public class MilvusOrm
{
	private static readonly Lazy<MilvusOrm> _lazy = new Lazy<MilvusOrm>(
		() => new MilvusOrm(
			Environment.GetEnvironmentVariable("MILVUS_HOST") ?? "10.193.40.136",
			int.Parse(Environment.GetEnvironmentVariable("MILVUS_PORT") ?? "19530", CultureInfo.InvariantCulture),
			bool.Parse(Environment.GetEnvironmentVariable("MILVUS_USE_SSL") ?? "false"),
			Environment.GetEnvironmentVariable("MILVUS_DATABASE") ?? "technical_economy"
		)
	);

	public static MilvusOrm instance => _lazy.Value;

	public MilvusClient _client { get; set; }
	public MilvusCollection? _collection { get; set; }

	private MilvusOrm(string host_ip, int host_port, bool use_ssl, string database_name)
	{
		this._client = new MilvusClient(
			host: host_ip,
			port: host_port,
			ssl: use_ssl,
			database: database_name
		);
	}


	//=================================
	/// <summary>
	/// 
	/// </summary>
	/// <returns></returns>
	public async Task<string> GetVersion()
	{
		// 检查Milvus服务器版本
		var version = await this._client.GetVersionAsync();
		Console.WriteLine($"Milvus server version: {version}");
		return version;
	}

	public async Task<IList<MilvusCollectionInfo>> GetCollectionInfos()
	{
		List<MilvusCollectionInfo> collectionInfos = (await _client.ListCollectionsAsync()).ToList();
		return collectionInfos;
	}

	public async Task<IList<string>> GetCollectionNames()
	{
		var collectionInfos =
			(await _client.ListCollectionsAsync())
			.ToList();
		return collectionInfos
			.Select(i => i.Name)
			.ToList();
	}

	public async Task<IList<MilvusCollection>> GetCollections()
	{
		var collectionInfos =
			(await _client.ListCollectionsAsync())
			.ToList();
		return collectionInfos
			.Select(i => _client.GetCollection(i.Name))
			.ToList();
	}

	//================================= 

	/// <summary>获得collection实体</summary>
	public async Task<MilvusCollection?> LoadCollection(string collectionName = "book")
	{
		bool hasCollection = await _client.HasCollectionAsync(collectionName);
		if (!hasCollection) { return null; }
		MilvusCollection collection = _client.GetCollection(collectionName);
		//加载集合：将集合数据从磁盘加载到内存，以便进行高效搜索。只有加载后的集合才能执行搜索操作
		await collection.LoadAsync();

		_collection = collection;
		return collection;
	}

	/// <summary>删除collection</summary>
	public async Task<int> DropCollection(string collectionName = "book")
	{
		//MilvusHealthState fieldDatas = await _client.HealthAsync();
		//Console.WriteLine("Connected"); 

		bool hasCollection = await _client.HasCollectionAsync(collectionName);
		if (!hasCollection) { return -1; }

		MilvusCollection collection = _client.GetCollection(collectionName);

		Console.WriteLine($"has collection {collectionName}:{hasCollection.ToString()}");

		if (hasCollection)
		{
			await collection.DropAsync();
			Console.WriteLine("drop collection {0}", collectionName);
			return 1;
		}
		else
		{
			Console.WriteLine("not has collection {0}", collectionName);
			return 0;
		}
	}

	/// <summary>创建collection</summary>
	public async Task<MilvusCollection> CreateCollection(string collectionName)
	{
		MilvusCollection milvusCollection =
		await _client.CreateCollectionAsync(
			collectionName,
			new[] {
					FieldSchema.Create<long>("book_id",isPrimaryKey:true),
					FieldSchema.Create<long>("word_count"),
					FieldSchema.CreateVarchar("book_name",512),
					FieldSchema.CreateFloatVector("book_intro",1024),
					FieldSchema.CreateJson("metadata")
					});
		return milvusCollection;
	}

	/// <summary>建立向量索引</summary>
	public async Task CreateIndex(MilvusCollection collection, string fieldName = "book_intro")
	{
		// 创建索引
		var indexParams = new Dictionary<string, string>
				{
					//{ "index_type", "IVF_FLAT" },
					//{ "metric_type", "L2" },
					{ "nlist", "128" }
				};

		if (collection == null) { return; }
		await collection.CreateIndexAsync(
			fieldName: fieldName,
			indexType: IndexType.IvfFlat,
			metricType: SimilarityMetricType.L2,
			extraParams: indexParams);

		//加载集合：将集合数据从磁盘加载到内存，以便进行高效搜索。只有加载后的集合才能执行搜索操作
		await collection.LoadAsync();
	}

	public async Task<IList<FieldSchema>> GetCollectionFields()
	{
		if (_collection == null) { return new List<FieldSchema>(); }
		// 调用 DescribeAsync 方法获取 collection 描述信息
		var collectionDescription = await _collection.DescribeAsync();

		// 提取所有 field 信息
		var fields = collectionDescription.Schema.Fields;

		// 输出每个 field 的信息
		foreach (var field in fields)
		{
			Console.WriteLine($"Field Name: {field.Name}");
			Console.WriteLine($"Data Type: {field.DataType}");
			Console.WriteLine($"Is Primary Key: {field.IsPrimaryKey}");
			Console.WriteLine($"Auto ID: {field.AutoId}");
			Console.WriteLine($"Is Partition Key: {field.IsPartitionKey}");
			Console.WriteLine($"Description: {field.Description}");
			Console.WriteLine($"Max Length: {field.MaxLength}");
			Console.WriteLine($"Dimension: {field.Dimension}");
			Console.WriteLine("---------------------");
		}
		return fields;
	}
	//=================================

	/// <summary>
	///
	///   
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <param name="expr"></param>
	/// <param name="queryParams"></param>
	/// <returns></returns>
	public async Task<IList<T>> QueryBy<T>(
		string expr = "",
		QueryParameters? queryParams = null)
	{
		if (queryParams == null)
		{
			queryParams = new QueryParameters
			{
				Limit = 20, // 返回最多20条结果
				Offset = 0, // 跳过前10条，从第11条开始
							// 最终一致性（查询更快）
				ConsistencyLevel = ConsistencyLevel.Eventually,
				// 配置需要返回的字段
				OutputFields = { "id" },
				// 配置需要查询的分区（可选）
				//PartitionNames = { "partition_2023", "partition_2024" },
				// 时间旅行参数（可选，根据需求设置）
				// 查询该时间戳对应的历史数据
				TimeTravelTimestamp = 1620000000000
			};
		}
		var result = await this._collection!.QueryAsync(expr, queryParams);
		FieldData fieldData = result
			.First();
		var datas = fieldData.GetData().CastTo<T>(fieldData.DataType);
		return datas;
	}

	/// <summary>
	///
	///  var describs = await MilvusOrm.instance.Query<string>(query_str, null, outputFields:  new List<string>() { "describ", "name_property" });
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <param name="expr"></param>
	/// <param name="range"></param>
	/// <param name="level"></param>
	/// <param name="outputFields"></param>
	/// <param name="partitionNames"></param>
	/// <returns></returns>
	public async Task<IList<T>> QueryBy<T>(
		string expr = "",
		int[]? range = null,
		ConsistencyLevel level = ConsistencyLevel.Eventually,
		List<string>? outputFields = null,
		List<string>? partitionNames = null
		)
	{
		if (range == null) { range = new int[] { 0, 16383 }; }
		if (outputFields == null) { outputFields = new List<string>() { "id" }; }
		if (partitionNames == null) { partitionNames = new List<string>() { }; }

		var queryParams = new QueryParameters
		{
			Limit = range[1] - range[0], // 返回最多20条结果
			Offset = range[0], // 跳过前10条，从第11条开始
							   // 最终一致性（查询更快）
			ConsistencyLevel = ConsistencyLevel.Eventually,

			// 时间旅行参数（可选，根据需求设置）
			// 查询该时间戳对应的历史数据
			TimeTravelTimestamp = 1620000000000
		};
		// 配置需要返回的字段
		// 为只读的OutputFields列表添加元素
		foreach (var field in outputFields)
		{
			queryParams.OutputFields.Add(field);
		}
		// 对PartitionNames做同样处理（如果它也是只读的）
		// 配置需要查询的分区（可选）
		//PartitionNames = { "partition_2023", "partition_2024" },
		foreach (var partition in partitionNames)
		{
			queryParams.PartitionNames.Add(partition);
		}

		var fieldDatas = await this._collection!.QueryAsync(expr, queryParams);

		FieldData fieldData = fieldDatas
			.Where(r => r.FieldName == outputFields.First())
			.First();

		var datas = fieldData.GetData().CastTo<T>(fieldData.DataType);
		return datas;
	}
 
	public async Task<SearchResults?> SearchBy(
		string vectorFieldName,
		List<ReadOnlyMemory<float>> searchVectors,
		string expr,
		SimilarityMetricType metricType = SimilarityMetricType.Ip,
		int topK = 10,
		List<string>? outputFields = null,
		List<string>? partitionNames = null,
		CancellationToken? token = null
		)
	{
		if (outputFields == null) { outputFields = new List<string>() { "id" }; }
		if (partitionNames == null) { partitionNames = new List<string>() { }; }

		CancellationToken cancellationToken = token ?? CancellationToken.None;
		//if (token == null) { token = CancellationToken.None; }

		try
		{
			// 1. 准备要搜索的向量（这里是float类型）
			// 假设我们要搜索2个向量，每个向量有128维

			//var searchVectors = new List<ReadOnlyMemory<float>>
			//{
			//    new float[] { 0.1f, 0.2f, 0.3f, /* ... 省略其他125个维度 ... */ }.AsMemory(),
			//    new float[] { 0.4f, 0.5f, 0.6f, /* ... 省略其他125个维度 ... */ }.AsMemory()
			//};

			// 2. 配置搜索参数（可选）
			var searchParams = new SearchParameters
			{
				// 过滤表达式（可选）- 类似于SQL的WHERE子句
				Expression = expr,
				// 一致性级别
				ConsistencyLevel = ConsistencyLevel.Strong,
				// 偏移量（用于分页）
				Offset = 0,
				// 结果保留的小数位数
				RoundDecimal = 4,
			};
			// 要返回的字段（除了向量字段外）
			// 配置需要返回的字段
			// 为只读的OutputFields列表添加元素
			foreach (var field in outputFields)
			{
				searchParams.OutputFields.Add(field);
			}
			// 要搜索的分区（可选）
			// 对PartitionNames做同样处理（如果它也是只读的）
			foreach (var partition in partitionNames)
			{
				searchParams.PartitionNames.Add(partition);
			}
			// 额外参数（根据需要添加）
			// 对于IVF索引，设置查询时的nprobe
			searchParams.ExtraParameters.Add("nprobe", "32");

			// 3. 执行搜索
			; // 每个查询向量返回前10个最相似的结果
			var results = await this._collection!.SearchAsync<float>(
				vectorFieldName: vectorFieldName, // 向量字段名
				vectors: searchVectors,             // 要搜索的向量列表
				metricType: metricType,             // 距离度量方式（L2欧氏距离）
				limit: topK,                        // 返回结果数量
				parameters: searchParams,           // 搜索参数
				cancellationToken: cancellationToken
			);

			// 4. 处理搜索结果
			//ProcessSearchResults(results);
			Console.WriteLine(results);
			return results;
		}
		catch (Exception ex)
		{
			Console.WriteLine($"搜索出错: {ex.Message}");
			return null;
		}

	}
	  
	//public async Task DFE()
	//{
	//   //var res =await this._collection!.HybridSearchEmbedAsync();
	//    return;
	//}

	//================================= 

	/// <summary>添加数据</summary>
	public async Task InsertDatas<T>(IEnumerable<T> datas)
	{
		if (datas.Count() == 0) { return; }
		var data = datas.First();
		if (data == null)
		{
			Console.WriteLine("data is null");
			return;
		}
		var props = data.GetType().GetProperties();

		var fieldDatas = new List<FieldData>();

		if (_collection == null) { return; }
		var fields = (await _collection.DescribeAsync()).Schema.Fields.ToList();

		foreach (var prop in props)
		{

			string fieldName = prop.Name;
			var prop_values = datas
				.Select(d => prop.GetValue(d))
				.Where(d => d != null)
				.ToList();

			var okFields = fields.Where(f => f.Name == fieldName).ToList();
			if (okFields.Count() == 0) { continue; }
			else
			{
				FieldData? fdata = null;

				var dataType = okFields.First().DataType;
				if (dataType == MilvusDataType.Int16 ||
					dataType == MilvusDataType.Int32 ||
					dataType == MilvusDataType.Int64
				)
				{
					IReadOnlyList<long> vals = prop_values
						.Select(v => v != null ? (long)v : 0)
						//.Select(v => (long)v)
						.ToList();
					fdata = FieldData.Create(fieldName, vals);
				}
				if (dataType == MilvusDataType.VarChar ||
					dataType == MilvusDataType.String
				)
				{
					var vals = prop_values
							.Select(v => v != null ? (string)v : "")
							//.Select(v => (string)v)
							.ToList();
					fdata = FieldData.CreateVarChar(fieldName, vals);
				}
				if (dataType == MilvusDataType.FloatVector)
				{
					var vals = prop_values
						.Select(v => v != null ? (ReadOnlyMemory<float>)v : new ReadOnlyMemory<float> { })
						//.Select(v => (ReadOnlyMemory<float>)v)
						.ToList();
					fdata = FieldData.CreateFloatVector(fieldName, vals);
				}
				if (dataType == MilvusDataType.Double)
				{
					var vals = prop_values
						.Select(v => v != null ? (double)v : 0)
						//.Select(v => (double)v)
						.ToList();
					fdata = FieldData.Create(fieldName, vals);
				}
				if (dataType == MilvusDataType.Float)
				{
					var vals = prop_values
						.Select(v => v != null ? (float)v : 0)
						//.Select(v => (float)v)
						.ToList();
					fdata = FieldData.Create(fieldName, vals);
				}
				if (dataType == MilvusDataType.Json)
				{
					var vals = prop_values
						.Select(v => v != null ? (string)v : "")
						//.Select(v => (string)v)
						.ToList();
					fdata = FieldData.CreateJson(fieldName, vals);
				}


				if (fdata != null)
				{
					fieldDatas.Add(fdata);
				}
			}
		}

	}

}

public class MilvusOrmDemo
{
	public async Task InsertDataDemo(MilvusCollection collection)
	{
		//insert数据
		Random ran = new();
		List<long> bookIds = new();
		List<long> wordCounts = new();
		List<ReadOnlyMemory<float>> bookIntros = new();
		List<string> bookNames = new();

		for (long i = 0L; i < 2000; ++i)
		{
			bookIds.Add(i);
			wordCounts.Add(i + 10000);
			bookNames.Add($"'Book Name {i}");

			float[] vector = new float[1024];
			for (int k = 0; k < 1024; ++k)
				vector[k] = ran.Next();
			bookIntros.Add(vector);
		}

		//MilvusCollection collection2 = milvusClient.GetCollection(collectionName);

		if (collection == null) { return; }
		MutationResult result2 = await collection.InsertAsync(new FieldData[]
			{
				FieldData.Create("book_id", bookIds),
				FieldData.Create("word_count", wordCounts),
				FieldData.Create("book_name", bookNames),
				FieldData.CreateFloatVector("book_intro", bookIntros),
			});
		// Check fieldDatas
		Console.WriteLine("Insert status:{0},", result2.ToString());

		//result2.Dump();

	}


	public async Task<IList<T>> QueryDataDemo<T>(MilvusCollection collection)
	{



		var queryParams = new QueryParameters
		{
			Limit = 20, // 返回最多20条结果
			Offset = 10, // 跳过前10条，从第11条开始 
			OutputFields = { "describ" },
			// 配置需要查询的分区（可选）
			//PartitionNames = { "partition_2023", "partition_2024" },
			// 时间旅行参数（可选，根据需求设置）
			TimeTravelTimestamp = 1620000000000 // 查询该时间戳对应的历史数据
		};

		string query_str = "category == \"国家电网\"";

		if (collection == null) { return new List<T>(); }
		var result = await collection!.QueryAsync(query_str, queryParams);
		Console.WriteLine($"fieldDatas count:{result.Count}");
		FieldData fieldData = result.First();
		var datas = fieldData.GetData().CastTo<T>(fieldData.DataType);
		return datas;
	}

}
